#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include "tcp_flow_ctrl_test.h"

#define SERVER_STR_ADDR "127.0.0.1"

static uint8_t s_recv_buff[SEND_BLOCK_SIZE] = {0};

int main(int argc, char const *argv[])
{
    int recv_bytes_cnt = 0;
    int send_bytes_cnt = 0;

    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    int win_size = SOCKET_WIN_SIZE;
    int ret = setsockopt(client_fd, SOL_SOCKET, SO_RCVBUF, &win_size, sizeof(win_size));
    if(0 > ret)
    {
        PERROR();
        goto fail;
    }
    ret = setsockopt(client_fd, SOL_SOCKET, SO_SNDBUF, &win_size, sizeof(win_size));
    if(0 > ret)
    {
        PERROR();
        goto fail;
    }

    struct sockaddr_in server_addr = 
    {
        .sin_family = AF_INET,
        .sin_addr.s_addr = inet_addr(SERVER_STR_ADDR),
        .sin_port = htons(SERVER_PORT),
    };
    ret = connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if(0 > ret)
    {
        PERROR();
        goto fail;
    }

    int bytes = 0;
    struct timeval start, end, intvl;
    gettimeofday(&start, NULL);
    while(1)
    {
        int bytes_in_block = 0;
        while(bytes_in_block < SEND_BLOCK_SIZE)
        {
            int bytes = recv(client_fd, s_recv_buff + bytes_in_block, SEND_BLOCK_SIZE - bytes_in_block, 0);
            if(bytes > 0)
            {
                PRINTF("recv %d bytes once", bytes);
                bytes_in_block += bytes;
                recv_bytes_cnt += bytes;
            }
            else if(0 == bytes)
            {
                PRINTF("server close socket");
                goto success;
            }
            else
            {
                PERROR();
                goto fail;
            }
        }
        PRINTF("recv one block");
        usleep(CLIENT_HDLE_INVL_USEC);
        bytes_in_block = 0;
        while(bytes_in_block < SEND_BLOCK_SIZE)
        {
            int bytes = send(client_fd, s_recv_buff + bytes_in_block, SEND_BLOCK_SIZE - bytes_in_block, 0);
            if(bytes > 0)
            {
                bytes_in_block += bytes;
                send_bytes_cnt += bytes;
            }
            else if(0 == bytes)
            {
                PRINTF("send 0 bytes");
            }
            else
            {
                PERROR();
                goto fail;
            }
        }
        PRINTF("send one block");
    }

success:
    gettimeofday(&end, NULL);
    timersub(&end, &start, &intvl);
    PRINTF("sum of recv %d bytes", recv_bytes_cnt);
    PRINTF("sum of send %d bytes", send_bytes_cnt);
    PRINTF("tcp flow control test client end spend %ld.%ld Sec", intvl.tv_sec, intvl.tv_usec / 1000);
    close(client_fd);
    return EXIT_SUCCESS;
fail:
    if(client_fd > 0)
    {
        close(client_fd);
    }
    return EXIT_FAILURE;
}
